package com.ruoyi.youtian.threadConfig;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.config.thread.AbstractWork;
import com.ruoyi.common.constant.YouQiConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.youtian.domain.FaultRecord;
import com.ruoyi.youtian.domain.FaultRecordDetail;
import com.ruoyi.youtian.domain.Template;
import com.ruoyi.youtian.domain.TemplateDetail;
import com.ruoyi.youtian.domain.vo.AbnormalVo;
import com.ruoyi.youtian.domain.vo.FaultRecordVO;
import com.ruoyi.youtian.domain.vo.TemplateSceneVO;
import com.ruoyi.youtian.domain.vo.TemplateVO;
import com.ruoyi.youtian.mapper.TemplateMapper;
import com.ruoyi.youtian.service.IFaultRecordService;
import com.ruoyi.youtian.service.ITemplateDetailService;
import com.ruoyi.youtian.service.ITemplateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.jni.Local;
import org.springframework.util.CollectionUtils;
import org.springframework.util.concurrent.FutureAdapter;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author li long
 * @Date 2022/9/19
 * @Version V1.0.0
 * @Description
 */
@Slf4j
public class AsyncAnalyseFault extends AbstractWork {


    private ITemplateService templateService;

    private IFaultRecordService faultRecordService;

    private ITemplateDetailService templateDetailService;

    private Long templateId;

    private Map<Long, TemplateSceneVO> faultNodeMap = new HashMap<>();


    public AsyncAnalyseFault(String workDescription, ITemplateService templateService, IFaultRecordService faultRecordService, ITemplateDetailService templateDetailService, Long templateId) {
        super(workDescription);
        this.templateService = templateService;
        this.faultRecordService = faultRecordService;
        this.templateDetailService = templateDetailService;
        this.templateId = templateId;
    }

    /**
     * 执行一个模型的错误分析流程
     * 1.查询模板对应的详情及 目标节点 上下限
     * 2. 执行异常分析
     * 3.保存异常分析快照
     */
    @Override
    public void process() {

        //1.查询模板对应的详情及 目标节点 上下限
        TemplateSceneVO templateSceneVO = new TemplateSceneVO();
        templateSceneVO.setTemplateId(templateId);
        List<TemplateSceneVO> templateSceneVOS = templateService.selectSceneTargetPoint(templateSceneVO);
        log.info("异常分析,templateId: {}", templateId);

        //2. 执行异常分析
        analyseFault(templateSceneVOS);

        //3.保存异常分析快照
        insertFaultRecord(templateId);

    }

    private void insertFaultRecord(Long templateId) {

        TemplateDetail templateDetail = new TemplateDetail();
        templateDetail.setTemplateId(templateId);
        List<TemplateDetail> templateDetails = templateDetailService.selectTemplateDetaiandTemplatelList(templateDetail);
        List<FaultRecordDetail> faultRecords = new ArrayList<>();
        Template template = templateService.getById(templateId);


        templateDetails.forEach(tmd -> {
            templateDetail.setTemplateName(tmd.getTemplateName());
            FaultRecordDetail faultRecordDetail = new FaultRecordDetail();
            BeanUtils.copyBeanProp(faultRecordDetail, tmd);
            if (Objects.nonNull(faultNodeMap.get(tmd.getTemplateDetailId()))) {
                faultRecordDetail.setFaultLocation("1");

            }


            faultRecords.add(faultRecordDetail);
        });


        FaultRecordVO faultRecordVO = new FaultRecordVO();
        AbnormalVo abnormalVo = new AbnormalVo();

        FaultRecord faultRecord = new FaultRecord();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String format = sdf.format(new Date());
        //去掉一个%s 对应的 templateId
        String recordName = String.format("%s异常,%s",templateDetail.getTemplateName(), format);
        faultRecord.setRecordName(recordName);
        faultRecord.setEdges(template.getEdges());
        faultRecord.setNodes(template.getNodes());
        faultRecord.setCreateTime(new Date());
        StringBuilder sb = new StringBuilder();

        faultNodeMap.values().forEach(vo -> {
            sb.append(String.format(vo.getEjdwName()+">"+vo.getZyqName()+"异常点位id:%s,上限%s,下限%s, 当前值:%s ;", vo.getTargetPointId(), vo.getUpperLimit(), vo.getLowerLimit(), vo.getPointValue()));
            abnormalVo.setSourcetag(vo.getTargetPointId());
            abnormalVo.setUpper_limit(vo.getUpperLimit());
            abnormalVo.setLower_limit(vo.getLowerLimit());
            abnormalVo.setCurrentvalue(vo.getPointValue());
            SimpleDateFormat shijian = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            abnormalVo.setCreate_time(shijian.format(new Date()));
        });
        faultRecord.setFaultDesc(sb.toString());
        faultRecordVO.setFaultRecord(faultRecord);
        faultRecordVO.setFaultRecordDetailList(faultRecords);
        if (!CollectionUtils.isEmpty(faultNodeMap)) {
            faultRecordService.insertRecordAndDetail(faultRecordVO);

            abnormalVo.setRecord_id(faultRecord.getRecordId());
            faultRecordService.insertRecordAbnormal(abnormalVo);
            log.info("保存数据 faultRecords: {}", JSONObject.toJSONString(faultRecords));
        }

    }


    /**
     * 异常分析
     *
     * @param templateSceneVOS 模板对应的详情及 目标节点 上下限 列表
     */
    private void analyseFault(List<TemplateSceneVO> templateSceneVOS) {
        // 校验: 如果集合为空, 返回
        if (CollectionUtils.isEmpty(templateSceneVOS)) {
            return;
        }
        //获取头节点所在的所有连接线 (可能含有多条)
        List<TemplateSceneVO> startVoList = templateSceneVOS.stream().filter(vo -> Objects.equals(vo.getSourceTop(), YouQiConstants.HEAD_NODE_TYPE)).collect(Collectors.toList());
        log.info("analyseFault: templateId为:{}, 头节点: {}", templateId, JSONObject.toJSONString(startVoList));

        // 根据源点位进行分组
        Map<String, List<TemplateSceneVO>> sourcePointMap = templateSceneVOS.stream().collect(Collectors.groupingBy(TemplateSceneVO::getPointId));
        log.info("analyseFault: sourcePointMap: {}", JSONObject.toJSONString(sourcePointMap));
        // 遍历 头节点连线
        startVoList.forEach(vo -> {
            //分析下一个节点
            analyseNextNode(vo, sourcePointMap);
        });

    }

    /**
     * 递归方法 分析下一个节点
     * 遇到异常数据 或 执行到末级节点, 结束递归
     *
     * @param templateSceneVO
     */
    private void analyseNextNode(TemplateSceneVO templateSceneVO, Map<String, List<TemplateSceneVO>> sourcePointMap) {
        //如果是 模型, 则解析模型
        if (Objects.equals(templateSceneVO.getSourceType(), YouQiConstants.TEMPLATE_TYPE)) {
            // 查询模型id 对应的节点详情
            TemplateSceneVO childTemplate = new TemplateSceneVO();
            childTemplate.setTemplateId(templateSceneVO.getSourceTemplateId());
            List<TemplateSceneVO> templateSceneVOS = templateService.selectSceneTargetPoint(childTemplate);
            //根据源节点 分组
            Map<String, List<TemplateSceneVO>> map = templateSceneVOS.stream().collect(Collectors.groupingBy(TemplateSceneVO::getPointId));
            // 模型的头节点
            String pointId = templateSceneVO.getPointId();
            // 获取头节点连线
            List<TemplateSceneVO> childHeadNodeList = map.get(pointId);

            if (childHeadNodeList != null) {
                childHeadNodeList.forEach(targetPoint -> {
                    analyseNextNode(targetPoint, map);
                });
            }
            //循环 分析

            // 如果 模型内有异常数据 则结束
            if (!CollectionUtils.isEmpty(faultNodeMap)) {
                return;
            }

        }


        /**
         * 目标节点的值 大于上限 或 小于下限 , 流程结束
         * faultNodeMap 负责记录异常节点
         */
        if ((templateSceneVO.getPointValue() > templateSceneVO.getUpperLimit() || templateSceneVO.getPointValue() < templateSceneVO.getLowerLimit())) {

            faultNodeMap.put(templateSceneVO.getTemplateDetailId(), templateSceneVO);
            return;
        }


        //目标节点(下一个节点)
        String pointId = templateSceneVO.getTargetPointId();

        //获取异常节点详情
        List<TemplateSceneVO> targetPointList = sourcePointMap.get(pointId);
        //查询不到下个节点, 说明为末级节点 ,执行结束
        if (CollectionUtils.isEmpty(targetPointList)) {
            return;
        }
        log.info("analyseNextNode: targetPointId {},targetPointList:{}", pointId, JSONObject.toJSONString(targetPointList));

        targetPointList.forEach(targetPoint -> {
            analyseNextNode(targetPoint, sourcePointMap);
        });

    }

}
